package dao

import (
	"distributed-locks/lib/db"
	"distributed-locks/lib/redsynclock"
	"distributed-locks/lib/sonyflakeid"
	"distributed-locks/model"
	"errors"
	"fmt"
)

type IStock interface {
	CreateStock(stock model.Stock) (id uint64, err error)
	UpdateStock(stockList []model.Stock) (err error)
	UpdateStockRedsYnc(stockList []model.Stock) (err error)
}

type Stock struct {
}

func (_ Stock) CreateStock(stock model.Stock) (id uint64, err error) {
	stock.Id = sonyflakeid.GetId()
	result := db.GetDB().Create(stock)
	if result.Error != nil {
		return 0, result.Error
	}
	return stock.Id, nil
}

func (_ Stock) UpdateStock(stockList []model.Stock) (err error) {
	_db := db.GetDB()

	// 开启事务
	tx := _db.Begin()
	for _, v := range stockList {
		for {
			// 1.查询商品库存
			stock := model.Stock{}
			result := _db.First(&stock, v.Id)
			if result.Error != nil {
				// 回滚事务
				tx.Rollback()
				return result.Error
			}
			if result.RowsAffected == 0 {
				// 回滚事务
				tx.Rollback()
				return errors.New("没有库存信息")
			}
			if stock.Qty == 0 {
				// 回滚事务
				tx.Rollback()
				return errors.New("暂无库存")
			}

			if stock.Qty < v.Qty {
				// 回滚事务
				tx.Rollback()
				return errors.New("库存不足")
			}

			// 2.执行扣减
			stock.Qty = stock.Qty - v.Qty
			// update stock set stock = stock - 1, version = version + 1 where id = 1 and version = version;
			// Select("Qty", "Version") 解决gorm默认不更新零值的问题，使 Qty 等于0的时候也能更新成功
			result = tx.Model(&model.Stock{}).Select("qty", "version").Where(model.Stock{Id: stock.Id, Version: stock.Version}).Updates(model.Stock{Qty: stock.Qty, Version: stock.Version + 1})
			if result.RowsAffected == 0 {
				// 扣减失败
				fmt.Println("扣减失败，继续重试")
			} else {
				// 扣减成功，结束循环
				break
			}
		}
	}
	// 提交事务
	tx.Commit()
	return nil
}

func (_ Stock) UpdateStockRedsYnc(stockList []model.Stock) (err error) {
	_db := db.GetDB()

	// 开启事务
	tx := _db.Begin()

	// 创建一个用于获取互斥锁的再同步实例。
	rs := redsynclock.GetRedsync()

	for _, v := range stockList {
		// 1.创建互斥锁对象
		mutex := rs.NewMutex(fmt.Sprintf("goods_%d", v.Id)) // 设置name，唯一

		// 2.获取锁
		if err = mutex.Lock(); err != nil {
			// 回滚事务
			tx.Rollback()
			return errors.New("获取分布式锁异常")
		}

		// 3.查询商品库存
		stock := model.Stock{}
		result := _db.First(&stock, v.Id)
		if result.Error != nil {
			// 回滚事务
			tx.Rollback()
			return result.Error
		}
		if result.RowsAffected == 0 {
			// 回滚事务
			tx.Rollback()
			return errors.New("没有库存信息")
		}
		if stock.Qty == 0 {
			// 回滚事务
			tx.Rollback()
			return errors.New("暂无库存")
		}
		if stock.Qty < v.Qty {
			// 回滚事务
			tx.Rollback()
			return errors.New("库存不足")
		}

		// 4.执行扣减
		stock.Qty = stock.Qty - v.Qty

		// 5.更新
		result = tx.Save(&stock)
		if result.Error != nil {
			// 回滚事务
			tx.Rollback()
			return result.Error
		}

		// 6.释放锁
		if ok, err := mutex.Unlock(); !ok || err != nil {
			// 回滚事务
			tx.Rollback()
			return errors.New("释放分布式锁异常")
		}
	}
	tx.Commit() // 提交修改

	return nil
}

func NewStock() IStock {
	return Stock{}
}
